#ifndef __RRC_MESSAGE_TYPES_H__INCLUDED__
#define __RRC_MESSAGE_TYPES_H__INCLUDED__

#include "RrcConstants.h"
#include "CallpAsnTypes.h"
#include "RrcMessageTypes.h"

#include <stdint.h>

typedef uint16_t PhysCellId;
const uint16_t MIN_CELL_BLACK = 1;
const uint16_t MAX_CELL_BLACK = 16;
typedef uint16_t EutraArfcnValue;
typedef int16_t  QRxLevMin;
typedef uint16_t TReselection;
typedef uint16_t ReselectionThreshold;
typedef uint16_t CellReselectionPriority;
const uint16_t MIN_CELL_INTER = 1;
const uint16_t MAX_CELL_INTER = 16;
typedef int8_t QQualMinR9;
typedef uint16_t ReselectionThresholdQR9;
const uint16_t MIN_RESELECTION_THRESHOLD_Q_R9 = 0;
const uint16_t MAX_RESELECTION_THRESHOLD_Q_R9 = 31;
const uint16_t MIN_CARRIER_FREQ = 1;
const uint16_t MAX_CARRIER_FREQ = 8;
const int8_t MIN_Q_QUAL_MIN_R9 = -34;
const int8_t MAX_Q_QUAL_MAX_R9 = -3;

enum RanType
{
    RAN_LTE = 0,
    RAN_GERAN = 1,
    RAN_UTRA_FDD = 2,
    RAN_UTRA_TDD = 3,
    RAN_CDMA2000_HRPD = 4,
    RAN_CDMA2000_1XRTT = 5,
    NUM_OF_RAN_TYPE
};

class RrcCellAccessRelatedInfo
{
public:
    RrcCellAccessRelatedInfo();
    bool operator==(const RrcCellAccessRelatedInfo& lhs) const;
    bool operator!=(const RrcCellAccessRelatedInfo& lhs) const;
    // copy ctor and assignment operator generated by compiler are good enough

    // Will be used by Oss, indicates number of significant bits
    static const uint16_t BITS_TRACK_AREA_CODE = 16;
    uint16_t trackingAreaCode_; //BIT STRING (SIZE (16))
};

inline RrcCellAccessRelatedInfo::RrcCellAccessRelatedInfo():
trackingAreaCode_(INIT_VALUE_16BITS)
{
}

class RrcSibMappingInfo
{
public:
    RrcSibMappingInfo();
    bool operator==(const RrcSibMappingInfo& lhs) const;
    bool operator!=(const RrcSibMappingInfo& lhs) const;

    // copy ctor and assignment operator generated by compiler are good enough

    static const uint16_t MIN_NUM_SIB_TYPE = 0;
    static const uint16_t MAX_NUM_SIB_TYPE = MAX_SI-1;

    // typedef as SibTypeList
    LteApp::R1::CommonList<SibType, MIN_NUM_SIB_TYPE, MAX_NUM_SIB_TYPE> value_;
};

inline RrcSibMappingInfo::RrcSibMappingInfo()
{}


class SchedulingInfo
{
public:
    SchedulingInfo();
    bool operator==(const SchedulingInfo& lhs) const;
    bool operator!=(const SchedulingInfo& lhs) const;

    Periodicity siPeriodicity_;

    RrcSibMappingInfo sibMappingInfo_;
};

inline SchedulingInfo::SchedulingInfo():
siPeriodicity_(NUM_OF_PERIODICITY)
{}


class RrcSchedulingInfoList
{
public:
    RrcSchedulingInfoList();
    bool operator==(const RrcSchedulingInfoList& lhs) const;
    bool operator!=(const RrcSchedulingInfoList& lhs) const;

    // copy ctor and assignment operator generated by compiler are good enough

    static const uint16_t MIN_SI_MESSAGE = 1;
    static const uint16_t MAX_SI_MESSAGE = 32;

    // typedef as SchedulingInfoList
    LteApp::R1::CommonList<SchedulingInfo, MIN_SI_MESSAGE, MAX_SI_MESSAGE> value_;
};

inline RrcSchedulingInfoList::RrcSchedulingInfoList()
{}

class RrcTddConfig
{
    public:
        RrcTddConfig();
        bool operator==(const RrcTddConfig& lhs) const;
        bool operator!=(const RrcTddConfig& lhs) const;

        RrcSubFrameAssignment subFrameAssign_;
        RrcSpecSubFramePatterns specSubFramePattern_;
};

inline RrcTddConfig::RrcTddConfig():
subFrameAssign_(NUM_SUB_FRM_ASSGN),
specSubFramePattern_(NUM_SUB_FRM_PATTERN)
{
}



class RrcSystemInformationBlockType1
{
public:
    RrcSystemInformationBlockType1();
    bool operator==(const RrcSystemInformationBlockType1& lhs) const;
    bool operator!=(const RrcSystemInformationBlockType1& lhs) const;

    static const uint16_t MIN_FREQUENCY_BAND_INDICATOR = 1;
    static const uint16_t MAX_FREQUENCY_BAND_INDICATOR = 64;
    static const uint16_t MIN_SYSINFO_VALTAG = 0;
    static const uint16_t MAX_SYSINFO_VALTAG = 31;

    RrcCellAccessRelatedInfo cellAccessRelatedInfo_;

    uint16_t freqBandIndicator_;

    RrcSchedulingInfoList schedulingInfoList_;

    RrcSiWindowLength siWindowLength_;

    // range: 0 ~ 31
    uint16_t systemInfoValueTag_;

    uint32_t plmnId_; //todo: now we choose the first in plmn-list

    bool isTddConfigPresent_;
    RrcTddConfig tddConfig_;
};

class MobilityStateParameters
{
public:
    MobilityStateParameters();

    bool operator== (const MobilityStateParameters& rhs) const;
    bool operator!= (const MobilityStateParameters& rhs) const;

    static const uint16_t MIN_NCELL_CHANGE_MED = 1;
    static const uint16_t MAX_NCELL_CHANGE_MED = 16;
    static const uint16_t MIN_NCALL_CHANGE_HIGH = 1;
    static const uint16_t MAX_NCALL_CHANGE_HIGH = 16;

    EvalHystNormal tEvaluation_;
    EvalHystNormal tHystNormal_;

    uint16_t nCellChangeMedium_;
    uint16_t nCellChangeHigh_;
};

inline MobilityStateParameters::MobilityStateParameters() :
tEvaluation_(NUM_EVAL_HYST_NORMAL),
tHystNormal_(NUM_EVAL_HYST_NORMAL),
nCellChangeMedium_(INIT_VALUE_16BITS),
nCellChangeHigh_(INIT_VALUE_16BITS)
{
}

class QHystSF
{
public:
    QHystSF();

    bool operator== (const QHystSF& rhs) const;
    bool operator!= (const QHystSF& rhs) const;

    HystSF hystSFMedium_;
    HystSF hystSFhigh_;
};


class SpeedStateReselectionPars
{
public:
    SpeedStateReselectionPars();

    bool operator== (const SpeedStateReselectionPars& rhs) const;
    bool operator!= (const SpeedStateReselectionPars& rhs) const;

    MobilityStateParameters mobilityStateParameters_;
    QHystSF qHystSf_;
};

inline SpeedStateReselectionPars::SpeedStateReselectionPars()
{}


class CellReselectionInfoCommon
{
public:
    CellReselectionInfoCommon();

    bool operator== (const CellReselectionInfoCommon& rhs) const;
    bool operator!= (const CellReselectionInfoCommon& rhs) const;

    QHyst qHyst_;

    bool isSpeedStateReselectionParsPresent_;
    SpeedStateReselectionPars speedStateReselectionPars_;
};

inline CellReselectionInfoCommon::CellReselectionInfoCommon():
qHyst_(NUM_Q_HYST),
isSpeedStateReselectionParsPresent_(false)
{}

class RrcSystemInformationBlockType3
{
public:
    RrcSystemInformationBlockType3();

    bool operator== (const RrcSystemInformationBlockType3& rhs) const;
    bool operator!= (const RrcSystemInformationBlockType3& rhs) const;

    CellReselectionInfoCommon cellReselectionInfoCommon_;
    //CellReselectionServingFreqInfo cellReselectionServingFreqInfo_;
    //IntraFreqCellReselectionInfo intraFreqCellReselectionInfo_;

    bool isLateNonCriticalExtensionPresent_;
    //LateNonCriticalExtension lateNonCriticalExtension_;

    bool isSIntraSearchV920Present_;
    //SIntraSearchV920 sIntraSearchV920_;

    bool isSNonIntraSearchV920Present_;
    //SNonIntraSearchV920 sNonIntraSearchV920_;

    bool isQQualMinR9Present_;
    //QQualMinR9 qQualMinR9_;

    bool isReselectionThresholdQR9Present_;
    //ReselectionThresholdQR9 threshServingLowQR9_;
};

inline RrcSystemInformationBlockType3::RrcSystemInformationBlockType3():
isLateNonCriticalExtensionPresent_(false),
isSIntraSearchV920Present_(false),
isSNonIntraSearchV920Present_(false),
isQQualMinR9Present_(false),
//qQualMinR9_(MAX_Q_QUAL_MAX_R9+1),
isReselectionThresholdQR9Present_(false)
//threshServingLowQR9_(MAX_RESELECTION_THRESHOLD_Q_R9+1)
{}

enum QOffsetRange
{
    DB_24 = 0,
    DB_22 = 1,
    DB_20 = 2,
    DB_18 = 3,
    DB_16 = 4,
    DB_14 = 5,
    DB_12 = 6,
    DB_10 = 7,
    DB_8 = 8,
    DB_6 = 9,
    DB_5 = 10,
    DB_4 = 11,
    DB_3 = 12,
    DB_2 = 13,
    DB_1 = 14,
    DB0 = 15,
    DB1 = 16,
    DB2 = 17,
    DB3 = 18,
    DB4 = 19,
    DB5 = 20,
    DB6 = 21,
    DB8 = 22,
    DB10 = 23,
    DB12 = 24,
    DB14 = 25,
    DB16 = 26,
    DB18 = 27,
    DB20 = 28,
    DB22 = 29,
    DB24 = 30,
    NUM_OF_Q_OFFSET_RANGE,
    MIN_Q_OFFSET_RANGE = DB_24,
    MAX_Q_OFFSET_RANGE = NUM_OF_Q_OFFSET_RANGE - 1
};


class IntraFreqNeighCellInfo
{
public:
    IntraFreqNeighCellInfo();

    bool operator== (const IntraFreqNeighCellInfo& rhs) const;
    bool operator!= (const IntraFreqNeighCellInfo& rhs) const;

    PhysCellId physCellId_;
    //QOffsetRange qOffsetCell_;
};

inline IntraFreqNeighCellInfo::IntraFreqNeighCellInfo():
physCellId_(INIT_VALUE_16BITS)
//qOffsetCell_(NUM_OF_Q_OFFSET_RANGE)
{}

enum PhysCellIdRangeEnum
{
    PHYS_CELL_ID_RANGE_N4 = 0,
    PHYS_CELL_ID_RANGE_N8 = 1,
    PHYS_CELL_ID_RANGE_N12 = 2,
    PHYS_CELL_ID_RANGE_N16 = 3,
    PHYS_CELL_ID_RANGE_N24 = 4,
    PHYS_CELL_ID_RANGE_N32 = 5,
    PHYS_CELL_ID_RANGE_N48 = 6,
    PHYS_CELL_ID_RANGE_N64 = 7,
    PHYS_CELL_ID_RANGE_N84 = 8,
    PHYS_CELL_ID_RANGE_N96 = 9,
    PHYS_CELL_ID_RANGE_N128 = 10,
    PHYS_CELL_ID_RANGE_N168 = 11,
    PHYS_CELL_ID_RANGE_N252 = 12,
    PHYS_CELL_ID_RANGE_N504 = 13,
    PHYS_CELL_ID_RANGE_SPARE2 = 14,
    PHYS_CELL_ID_RANGE_SPARE1 = 15,
    NUM_OF_PHYS_CELL_ID_RANGE,
    MIN_PHYS_CELL_ID_RANGE = PHYS_CELL_ID_RANGE_N4,
    MAX_PHYS_CELL_ID_RANGE = NUM_OF_PHYS_CELL_ID_RANGE -1
};


class PhysCellIdRange
{
public:
    PhysCellIdRange();

    bool operator== (const PhysCellIdRange& rhs) const;
    bool operator!= (const PhysCellIdRange& rhs) const;
    PhysCellIdRange& operator = (const PhysCellIdRange& rhs);

    PhysCellId start_; //range (0..503)
    bool isRangePresent_;
    PhysCellIdRangeEnum range_;
};


class RrcSystemInformationBlockType4
{
public:
    RrcSystemInformationBlockType4();

    bool operator== (const RrcSystemInformationBlockType4& rhs) const;
    bool operator!= (const RrcSystemInformationBlockType4& rhs) const;

    bool isIntraFreqNeighCellListPresent_;
    LteApp::R1::CommonList<IntraFreqNeighCellInfo, MIN_CELL_INTRA, MAX_CELL_INTRA>
        intraFreqNeighCellList_;

    //bool isIntraFreqBlackCellListPresent_;
    //LteApp::R1::CommonList<PhysCellIdRange, MIN_CELL_BLACK, MAX_CELL_BLACK>
    //    intraFreqBlackCellList_;
};

inline RrcSystemInformationBlockType4::RrcSystemInformationBlockType4():
isIntraFreqNeighCellListPresent_(false),
intraFreqNeighCellList_()
//isIntraFreqBlackCellListPresent_(false),
//intraFreqBlackCellList_()
{}
        
class SpeedStateScaleFactors
{
public:
    SpeedStateScaleFactors();

    static const EutranSF DEFAULT_EUTRAN_SF = EUTRAN_SF_1_DOT0;

    bool operator== (const SpeedStateScaleFactors& rhs) const;
    bool operator!= (const SpeedStateScaleFactors& rhs) const;

    EutranSF sfMedium_;
    EutranSF sfHigh_;
};

class InterFreqNeighCellInfo
{
public:
    InterFreqNeighCellInfo();

    bool operator== (const InterFreqNeighCellInfo& rhs) const;
    bool operator!= (const InterFreqNeighCellInfo& rhs) const;

    PhysCellId physCellId_;
    //QOffsetRange qOffsetCell_;
};

inline InterFreqNeighCellInfo::InterFreqNeighCellInfo():
physCellId_(INIT_VALUE_16BITS)
//qOffsetCell_(NUM_OF_Q_OFFSET_RANGE)
{}

class ThreshXQR9
{
public: 
    ThreshXQR9();

    ReselectionThresholdQR9 threshXHighQR9_;
    ReselectionThresholdQR9 threshXLowQR9_;
};

inline ThreshXQR9::ThreshXQR9():
threshXHighQR9_(MAX_RESELECTION_THRESHOLD_Q_R9+1),
threshXLowQR9_(MAX_RESELECTION_THRESHOLD_Q_R9+1)
{
}

class InterFreqCarrierFreqInfo
{
public:
    InterFreqCarrierFreqInfo();

    bool operator== (const InterFreqCarrierFreqInfo& rhs) const;
    bool operator!= (const InterFreqCarrierFreqInfo& rhs) const;

    static const QOffsetRange DEFAULT_OFFSET_FREQ = DB0;
    static const ReselectionThreshold DEFAULT_EUTRAN_RESELECTION_THRESHOLD_HIGH = 1;
    static const ReselectionThreshold DEFAULT_EUTRAN_RESELECTION_THRESHOLD_LOW = 2;

    EutraArfcnValue dlCarrierFreq_;

    //QRxLevMin qRxLevMin_;

    //bool isPMaxPresent_;
    //int16_t pMax_; //range MIN_PMAX ~ MAX_PMAX

    //TReselection tReselectionEutra_; //range (0..7)

    //bool isTReselectionEutraSfPresent_;
    //SpeedStateScaleFactors tReselectionEutraSf_;

    //ReselectionThreshold threshXHigh_;

    //ReselectionThreshold threshXLow_;

    //MeasurementBandwidth allowedMeasBandwidth_;

    //bool presenceAntennaPort1_;

    //bool isCellReselectionPriorityPresent_;
    //CellReselectionPriority cellReselectionPriority_;

    //uint8_t neighCellConfig_; //BIT STRING (SIZE(2))

    //QOffsetRange qOffsetFreq_;

    bool isInterFreqNeighCellListPresent_;
    LteApp::R1::CommonList<InterFreqNeighCellInfo, MIN_CELL_INTER, MAX_CELL_INTER>
        interFreqNeighCellList_;

    //bool isInterFreqBlackCellListPresent_;
    //LteApp::R1::CommonList<PhysCellIdRange, MIN_CELL_BLACK, MAX_CELL_BLACK> 
    //    interFreqBlackCellList_;

    //bool isQQualMinR9Present_;
    //QQualMinR9 qQualMinR9_;

    //bool isThreshXQR9Present_;
    //ThreshXQR9 threshXQR9_;
};

inline InterFreqCarrierFreqInfo::InterFreqCarrierFreqInfo():
dlCarrierFreq_(INIT_VALUE_16BITS),
//qRxLevMin_(INIT_VALUE_16BITS),
//isPMaxPresent_(false),
//pMax_(INIT_VALUE_16BITS),
//tReselectionEutra_(0xff),
//isTReselectionEutraSfPresent_(false),
//tReselectionEutraSf_(),
//threshXHigh_(INIT_VALUE_16BITS),
//threshXLow_(INIT_VALUE_16BITS),
//allowedMeasBandwidth_(NUM_OF_MEAS_BW),
//presenceAntennaPort1_(false),
//isCellReselectionPriorityPresent_(false),
//cellReselectionPriority_(INIT_VALUE_16BITS),
//neighCellConfig_(0xff),
//qOffsetFreq_(DEFAULT_OFFSET_FREQ),
isInterFreqNeighCellListPresent_(false),
interFreqNeighCellList_()
//isInterFreqBlackCellListPresent_(false),
//interFreqBlackCellList_(),
//isQQualMinR9Present_(false),
//qQualMinR9_(MAX_Q_QUAL_MAX_R9+1),
//isThreshXQR9Present_(false)
{}

class RrcSystemInformationBlockType5
{
public:
    RrcSystemInformationBlockType5();

    bool operator== (const RrcSystemInformationBlockType5& rhs) const;
    bool operator!= (const RrcSystemInformationBlockType5& rhs) const;

    LteApp::R1::CommonList<InterFreqCarrierFreqInfo, MIN_CARRIER_FREQ, MAX_CARRIER_FREQ>
        interFreqCarrierFreqList_;
};

template<class T> class TypeSelector
{
};


class RrcSystemInformation
{
public:
    RrcSystemInformation();

    enum SibChoice
    {
        SIB_NOT_SPECIFIED = 0,
        SIB3_CHOSEN = 2,
        SIB4_CHOSEN = 3,
        SIB5_CHOSEN = 4,
        MAX_SIB_CHOICE
    };

    // Invoked to release resource allocated for Sib objects inside.
    // REMEMBER to invoke this function, if you want to REUSE the object.
    void reset();

    uint16_t getNumSibs() const;

    SibChoice getChoiceOfSib(uint16_t index) const;

    template<class T> 
    const T* getSibForRead(SibChoice choice) const
    {
        for(uint16_t i=0; i<numOfSib_; i++)
        {
            if( choice_[i]==choice )
            {
                return getForRead(TypeSelector<T>());
            }
        }
        return NULL;
    }

    template<class T> T* getNewSibForWrite()
    {
        return getForWrite(TypeSelector<T>());
    }

    RrcSystemInformation& operator= (const RrcSystemInformation&);

private:
    // prohibit copy ctor and assignment
    RrcSystemInformation(const RrcSystemInformation&);

   const RrcSystemInformationBlockType3*
        getForRead(TypeSelector<RrcSystemInformationBlockType3>) const;
    const RrcSystemInformationBlockType4*
        getForRead(TypeSelector<RrcSystemInformationBlockType4>) const;
    const RrcSystemInformationBlockType5*
        getForRead(TypeSelector<RrcSystemInformationBlockType5>) const;

    RrcSystemInformationBlockType3* getForWrite(TypeSelector<RrcSystemInformationBlockType3>);
    RrcSystemInformationBlockType4* getForWrite(TypeSelector<RrcSystemInformationBlockType4>);
    RrcSystemInformationBlockType5* getForWrite(TypeSelector<RrcSystemInformationBlockType5>);
    
    SibChoice choice_[MAX_SIB_CHOICE];
    bool isSibAddedInMessage_[MAX_SIB_CHOICE];

    RrcSystemInformationBlockType3 sib3_;
    RrcSystemInformationBlockType4 sib4_;
    RrcSystemInformationBlockType5 sib5_;

    uint16_t numOfSib_;
};

inline RrcSystemInformation::RrcSystemInformation():
numOfSib_(0)
{
    // Remember to initialize the choice to UNSPEC ones
    for (uint16_t uIt = 0; uIt < MAX_SIB_CHOICE; ++uIt)
    {
        choice_[uIt] = SIB_NOT_SPECIFIED;
        isSibAddedInMessage_[uIt] = false;
    }
}
inline RrcSystemInformation& RrcSystemInformation::operator= (const RrcSystemInformation& lhs)
{
    memcpy(this, &lhs, sizeof(RrcSystemInformation));
    return *this;
}

#endif

